package com.yys.apm.boot;

import com.yys.apm.boot.spi.ServiceSPI;
import com.yys.apm.config.ApmConfig;
import com.yys.apm.logger.ILogger;
import com.yys.apm.logger.LoggerManager;
import com.yys.apm.service.ApmService;

import java.lang.instrument.Instrumentation;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Yingsheng Ye
 * @date 2021-03-19 15:58
 */

public class ApmAgentManager {

    private volatile static ApmAgentManager INSTANCE = null;

    private static final ILogger logger = LoggerManager.getLogger(ApmAgentManager.class);

    private final static AtomicInteger ACCESS_COUNT = new AtomicInteger(0);

    public Instrumentation instrumentation;

    public Properties properties;

    private Map<Class, ApmService> services = Collections.emptyMap();

    private ApmAgentManager() {

    }

    public static ApmAgentManager getInstance() {
        if (null == INSTANCE) {
            synchronized (ApmAgentManager.class) {
                if (null == INSTANCE) {
                    INSTANCE = new ApmAgentManager();
                }
            }
        }
        return INSTANCE;
    }

    public void addAccessCount() {
        ACCESS_COUNT.addAndGet(1);
    }

    /**
     * 是否抽数
     *
     * @return boolean
     */
    public boolean isExtractData() {
        String samplingFlag = properties.getProperty(ApmConfig.SAMPLING_FLAG);
        if (!Boolean.parseBoolean(samplingFlag)) {
            return true;
        }
        int samplingRate = Integer.parseInt(properties.getProperty(ApmConfig.SAMPLING_RATE));
        return ACCESS_COUNT.get() % samplingRate == 0;
    }

    public void boot(Instrumentation instrumentation, Properties properties) {
        this.instrumentation = instrumentation;
        this.properties = properties;
        services = loadServices();
        this.prepare();
        this.startup();
        this.onComplete();
    }

    private void prepare() {
        for (ApmService service : services.values()) {
            try {
                service.prepare();
            } catch (Exception e) {
                logger.error(e, "ApmAgentManager prepare : [%s]  fail,%s", service.getClass().getSimpleName(), e.getMessage());
            }
        }
    }

    private void startup() {
        for (ApmService service : services.values()) {
            try {
                service.startup();
            } catch (Exception e) {
                logger.error(e, "ApmAgentManager startup : [%s]  fail,%s", service.getClass().getSimpleName(), e.getMessage());
            }
        }
    }

    private void onComplete() {
        for (ApmService service : services.values()) {
            try {
                service.onComplete();
            } catch (Exception e) {
                logger.error(e, "ApmAgentManager onComplete : [%s]  fail,%s", service.getClass().getSimpleName(), e.getMessage());
            }
        }
    }

    public void shutdown() {
        for (ApmService service : services.values()) {
            try {
                service.shutdown();
            } catch (Exception e) {
                logger.error(e, "ApmAgentManager shutdown : [%s]  fail,%s", service.getClass().getSimpleName(), e.getMessage());
            }
        }
    }

    private Map<Class, ApmService> loadServices() {
        Map<Class, ApmService> services = new LinkedHashMap<>();
        for (ApmService service : ServiceLoader.load(ApmService.class)) {
            Class<? extends ApmService> serviceClass = service.getClass();
            boolean isService = serviceClass.isAnnotationPresent(ServiceSPI.class);
            if (isService && !services.containsKey(serviceClass)) {
                services.put(serviceClass, service);
            }
        }
        return services;
    }

}
